package top.hekun.study.core;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import top.hekun.study.core.util.ClassFileUtil;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;

import static org.objectweb.asm.Opcodes.*;


/**
 * 〈ClassWriter 类实例〉 生成一个类
 *
 * @author: Sakura
 * @date: 2023/4/30
 */
public class ClassWriterGenerationToClass {

    public static void main(String[] args) throws IOException {
        File classFile = getClassFile();
        ClassWriter classWriter = getClassWriter();
        generateClassInfo(classWriter);
        generateClassFieldInfo(classWriter);
        generateClassMethodInfo(classWriter);
        byte[] classFileCore = getClassFileCore(classWriter);
        writeClassFileCore(classFileCore, classFile);
    }

    /**
     * 生成接口类信息
     *
     * @param classWriter
     */
    private static void generateClassInfo(ClassWriter classWriter) {


        /**
         *   Opcodes.V1_8, // jdk 版本
         *  Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER |,  // 类表示 使用+ 和| 运算 都可以 , 此处定义了 类接口
         *  "top/hekun/study/four/HelloWord",  // 类名称 全路径名称
         *  null,   // 泛型
         *  "java/lang/Object", 父类
         *   new String[]{"a/b/c/d0","a/b/c/d1"}  继承的接口
         */
        classWriter.visit(
                V1_8,
                ACC_PUBLIC | ACC_SUPER,
                "top/hekun/study/classFileModel/HelloWordClass",
                null,
                "java/lang/Object",
                null
        );


    }

    /**
     * 生成字段信息 access–字段的访问标志（请参阅操作代码）。此参数还指示字段是合成的和/或不推荐使用的。 name–字段的名称。 descriptor–字段的描述符（请参见类型）。
     * 签名–字段的签名。如果字段的类型不使用泛型类型，则可能为null。 value–字段的初始值。如果字段没有初始值，则此参数可能为null， 它必须是Integer、Float、Long、Double或String（分别用于int、Float、Long或String字段）
     * 此参数仅用于静态字段。对于非静态字段，它的值将被忽略， 这些字段必须通过构造函数或方法中的字节码指令进行初始化。
     *
     * @param classWriter
     */
    private static void generateClassFieldInfo(ClassWriter classWriter) {
        {
            FieldVisitor fieldVisitor = classWriter.visitField(
                    ACC_PUBLIC | ACC_FINAL | ACC_STATIC,
                    "age",
                    "I",
                    null,
                    22
            );
            fieldVisitor.visitEnd();
        }
        {
            FieldVisitor fieldVisitor = classWriter.visitField(
                    ACC_PUBLIC | ACC_FINAL | ACC_STATIC,
                    "name",
                    "Ljava/lang/String;",
                    null,
                    "坤坤"
            );
            // 添加注解
            AnnotationVisitor annotationVisitor = fieldVisitor.visitAnnotation("Ltop/hekun/study/annotation/MyAnnotation;", false);
            annotationVisitor.visit("name", "这是姓名");
            annotationVisitor.visit("age", Integer.valueOf(6));
            annotationVisitor.visitEnd();
            fieldVisitor.visitEnd();
        }
    }

    /**
     * 生成方法
     *
     * @param classWriter
     */
    private static void generateClassMethodInfo(ClassWriter classWriter) {
        {
            // 无参 构造方法
            MethodVisitor methodVisitor = classWriter.visitMethod(
                    ACC_PUBLIC,
                    "<init>",
                    "()V",
                    null,
                    null
            );
            methodVisitor.visitCode();
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitMethodInsn(INVOKESPECIAL,
                    "java/lang/Object",
                    "<init>",
                    "()V",
                    false
            );
            methodVisitor.visitInsn(RETURN);
            methodVisitor.visitMaxs(1, 1);
            methodVisitor.visitEnd();
        }
        {
            // 静态代码块
            MethodVisitor methodVisitor = classWriter.visitMethod(
                    ACC_STATIC,
                    "<clinit>",
                    "()V",
                    null,
                    null
            );
            methodVisitor.visitEnd();
        }
        {
            // 创建一个 叫test的方法
            MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
            // 编写代码
            methodVisitor.visitCode();

            methodVisitor.visitIntInsn(BIPUSH, 99);
            methodVisitor.visitVarInsn(ISTORE, 1);
            methodVisitor.visitIntInsn(BIPUSH, 77);
            methodVisitor.visitVarInsn(ISTORE, 2);
            methodVisitor.visitVarInsn(ILOAD, 1);
            methodVisitor.visitVarInsn(ILOAD, 2);
            methodVisitor.visitInsn(IADD);
            methodVisitor.visitVarInsn(ISTORE, 3);
            // java.io.PrintStream.println(java.lang.String)
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitVarInsn(ILOAD, 3);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
            methodVisitor.visitInsn(RETURN);
            methodVisitor.visitMaxs(2, 4);
            // 结束
            methodVisitor.visitEnd();
        }
        {
            MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "setName", "(Ljava/lang/String;)Ljava/lang/String;", null, null);
            methodVisitor.visitCode();
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitInsn(ARETURN);
            methodVisitor.visitMaxs(1, 1);
            methodVisitor.visitEnd();
        }
        {
            // 创建一个 叫test的方法
            MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "test1", "()V", null, null);
            // 编写代码
            methodVisitor.visitCode();

            methodVisitor.visitIntInsn(BIPUSH, 99);
            methodVisitor.visitVarInsn(ISTORE, 1);
            methodVisitor.visitIntInsn(BIPUSH, 77);
            methodVisitor.visitVarInsn(ISTORE, 2);
            methodVisitor.visitVarInsn(ILOAD, 1);
            methodVisitor.visitVarInsn(ILOAD, 2);
            methodVisitor.visitInsn(IADD);
            methodVisitor.visitVarInsn(ISTORE, 3);

            // 加载方法
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            // 加载角标0的变量
            methodVisitor.visitVarInsn(ALOAD, 0);
            // 调用执行
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
//
//            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
//            methodVisitor.visitVarInsn(ILOAD, 1);
//            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
//
//            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
//            methodVisitor.visitVarInsn(ILOAD, 2);
//            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
//
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitVarInsn(ILOAD, 3);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);

            methodVisitor.visitInsn(RETURN);
            methodVisitor.visitMaxs(2, 4);
            // 结束
            methodVisitor.visitEnd();
        }
        {
            MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "test1", "(II)V", null, null);
            methodVisitor.visitCode();
            methodVisitor.visitVarInsn(ILOAD, 1);
            methodVisitor.visitVarInsn(ILOAD, 2);
            methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/Math", "max", "(II)I", false);
            methodVisitor.visitVarInsn(ISTORE, 3);
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitVarInsn(ILOAD, 3);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
            methodVisitor.visitInsn(RETURN);
            methodVisitor.visitMaxs(2, 4);
        }
        {
            // if else 方法 示例  Label类的示例
            MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "test2", "(II)V", null, null);
            methodVisitor.visitCode();
            // 加载第一个参数和第二个参数
            methodVisitor.visitVarInsn(ILOAD, 1);
            methodVisitor.visitVarInsn(ILOAD, 2);
            // 创建一个标记
            Label label0 = new Label();
            // 判断两个数, 如果条件成立,跳转到`label0` 标记的位置
            // IF_ICMPLE 指令表示 比较两个int  value1 ≤ value2 即为成功
            methodVisitor.visitJumpInsn(IF_ICMPLE, label0);
            // 条件不成立 依次往下执行 执行打印第一个参数
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitVarInsn(ILOAD, 1);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
            // 在创建一个标记
            Label label1 = new Label();
            // 执行到这里 直接跳转到 `label1` 标记的位置
            methodVisitor.visitJumpInsn(GOTO, label1);
            // `label0` 标记的位置
            methodVisitor.visitLabel(label0);
            // 执行打印 参数2
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitVarInsn(ILOAD, 2);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
            // `label1` 标记的位置
            methodVisitor.visitLabel(label1);
            // return 退出
            methodVisitor.visitInsn(RETURN);
            methodVisitor.visitMaxs(2, 3);
            methodVisitor.visitEnd();
        }
        {
            // if else 方法 示例  Label类的示例
            MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "test3", "()V", null, null);
            methodVisitor.visitCode();

            // 创建一个标记
            Label label0 = new Label();
            Label label1 = new Label();
            // 条件不成立 依次往下执行 执行打印第一个参数
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitLdcInsn("开始");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

            methodVisitor.visitLabel(label0);

            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitLdcInsn("标记1");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

            // 执行到这里 直接跳转到 `label1` 标记的位置
            methodVisitor.visitJumpInsn(GOTO, label1);
            // 这部分代码会被忽略, 生成的代码,不会计算到这部分
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitLdcInsn("标记0.5");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

            methodVisitor.visitLabel(label1);
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitLdcInsn("标记0");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

            // 执行到这里 直接跳转到 `label1` 标记的位置
            methodVisitor.visitJumpInsn(GOTO, label0);



            // return 退出
            methodVisitor.visitInsn(RETURN);
            methodVisitor.visitMaxs(2, 3);
            methodVisitor.visitEnd();
        }
        {
            Method method = Method.getMethod("void main (String[])");
            GeneratorAdapter generatorAdapter = new GeneratorAdapter(ACC_PUBLIC|ACC_STATIC,method,null,null,classWriter);
            generatorAdapter.getStatic(Type.getType(System.class),"out",Type.getType(PrintStream.class));
            generatorAdapter.push("HelloWord,GeneratorAdapter生成!!");
            generatorAdapter.invokeVirtual(Type.getType(PrintStream.class),Method.getMethod("void println (String)"));
            generatorAdapter.returnValue();
            generatorAdapter.endMethod();
        }
    }


    /**
     * 获取类文件
     *
     * @return
     */
    private static File getClassFile() {
        System.out.println();
        String className = "HelloWordClass";
        File file = new File(ClassFileUtil.getClassFileModelPath(), className + ".class");
        return file;
    }

    /**
     * 获取 ClassWriter 对象
     *
     * @return
     */
    private static ClassWriter getClassWriter() {
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        return classWriter;
    }

    /**
     * 获取类的字节码 注: 本方法会自动调用visitEnd()
     *
     * @param classWriter
     *
     * @return
     */
    private static byte[] getClassFileCore(ClassWriter classWriter) {
        classWriter.visitEnd();
        byte[] core = classWriter.toByteArray();
        return core;

    }

    /**
     * 文件写入数据
     *
     * @param core
     * @param classFilePath
     */
    private static void writeClassFileCore(byte[] core, File classFilePath) {
        ClassFileUtil.classFileCoreWrite(core, classFilePath);
    }


}
